package net.mingsoft.flk.gmcryptojni;

import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.ClassPathResource;
import org.springframework.util.Assert;

import java.io.*;
import java.util.Arrays;

@Slf4j
public class GMCrypto {
    // TODO: 更换软件密码模块路径
    public static String GM_CRYPTO_MODULE_WINDOWS_FILE_PATH = "D:\\work_area\\zk-cms\\ms-mcms\\flk\\GMCrypto.dll";//"G:\\GMCrypto.dll";
    public static String GM_CRYPTO_MODULE_LINUX_FILE_PATH = "/home/java/zk-cms/slk/GMCrypto-arm920.so";

    static { loadCryptoModuleBy(""); }

    public static int loadCryptoModuleBy(String filePath) {
        if (null == filePath || filePath.isEmpty()) {
            if (System.getProperty("os.name").toLowerCase().contains("win")) {
                filePath = GM_CRYPTO_MODULE_WINDOWS_FILE_PATH;
            } else {
                filePath = GM_CRYPTO_MODULE_LINUX_FILE_PATH;
            }
        }
        return loadCryptoModuleBy(new File(filePath));
    } // loadCryptoModuleBy()

    public static int loadCryptoModuleBy(final File file) {
        try {
            Assert.isTrue(file.exists(), () -> "动态链接库 " + file.getPath() + "不存在");
            System.load(file.getPath());
            log.info("成功加载弗兰科密码模块({})。", file.getPath());
            return 0;
        } catch (Throwable ex) {
            log.error("加载弗兰科密码模块({}) 时发生了错误 ", file.getPath(), ex);
            return -1;
        }
    } // loadCryptoModuleBy()
//======================================================================================================================

    public static void main(String[] args) throws Exception {
        final ClassPathResource clazzPathRes = new ClassPathResource("flk/GMCrypto.dll");
        log.info("clazzPathRes: {} {}", clazzPathRes.getFile().exists(), clazzPathRes.getFile());
        final File file = new File("e:/tmp2/flk/GMCrypto.dll");
        loadCryptoModuleBy(file);
    } // main()
//======================================================================================================================

    public static native  int initGMKMSModule(Object jobj);
    public static native  int uninitGMKMSModule();

    private native int getKEK(byte[] jkeyVersion, byte[] jderiveKeyId);
    public native int delKEK(int keyid);

    private native int smMac(int keyid, byte[] jpucIV, byte[] jplaindata, int plaindatalen,byte[] jmac);
    private native int hmacSm3(int keyid, byte[] jplaindata, int plaindatalen,byte[] jmac);

    private native int smEncryptByKEK(int keyid, int uiAlgID, byte[] jpucIV, byte[] jplaindata, int plainLen,byte[] jcipherdata);
    private native int smDecryptByKEK(int keyid, int uiAlgID, byte[] jpucIV, byte[] jcipherdata,int cipherLen, byte[] jplaindata);

    public native int smEncryptByDerivedKey(int keyid, byte[] jderiveKeyId, int uiAlgID, byte[] jpucIV, byte[] jplaindata, int plaindatalen,byte[] jcipherdata);
    public native int smDecryptByDerivedKey(int keyid, byte[] jderiveKeyId, int uiAlgID, byte[] jpucIV, byte[] jcipherdata,int cipherdatalen,byte[] jplaindata);

    //协同签名接口
    public static native  int initGMCryptoModule(GMCryptoModulePara para);
    public static native  int uninitGMCryptoModule();

    public native int getSyncKeyPairs(ECCrefPublicKey pubkey);
    public native int  delSyncKeyPairs();

    private native int sm3MereHash(byte[] plaindata, int plainlen, byte[] hash);
    private native int sm3Hash2Sign(byte[] plaindata, int plainlen, byte[] pubX,byte[] pubY, byte[] userid, int useridlen, byte[] hash);

    private native int sm2Sign(byte[] hash, byte[] r,byte[] s);
    private native int sm2Verify(byte[] pubX,byte[] pubY, byte[] hash, byte[] r,byte[] s);

    public GmKey getKey(String keyVersion,String deriveKeyId){
        GmKey gmKey = new GmKey();
        byte[] keyVersionByte;
        byte[] derivekeyidByte;

        int keyVersionLength = 32;
        int maxDeriveKeyIdLen = 64;

        keyVersionByte = new byte[keyVersionLength];
        Arrays.fill(keyVersionByte,(byte)0);

        derivekeyidByte = new byte[maxDeriveKeyIdLen+1];
        Arrays.fill(derivekeyidByte,(byte)0);
        if (keyVersion == null){

        }else{
            if (keyVersion.getBytes().length>keyVersionLength){
                gmKey.keyId = -1;
                return gmKey;
            }
            System.arraycopy(keyVersion.getBytes(),0,keyVersionByte,0,keyVersion.getBytes().length);
        }

        if(deriveKeyId == null){

        }else{
            if(deriveKeyId.getBytes().length >= 64){
                gmKey.keyId = -1;
                return gmKey;
            }
            System.arraycopy(deriveKeyId.getBytes(),0,derivekeyidByte,0,deriveKeyId.getBytes().length);
        }

        int keyId = getKEK(keyVersionByte,derivekeyidByte);
        gmKey.keyId = keyId;
        if (keyId>0){
            String keyVersionRet = new String(keyVersionByte,0,getValidLength(keyVersionByte)-1);
            gmKey.keyVersion = keyVersionRet;
            if (deriveKeyId != null){
                gmKey.deriveKeyId = deriveKeyId;
            }else{
                gmKey.deriveKeyId = null;
            }
        }else{
            gmKey.keyId = keyId;
            gmKey.keyVersion = null;
            gmKey.deriveKeyId = null;
        }

        return gmKey;
    }

    public int getValidLength(byte[] bytes){
        int i = 0;
        if (null == bytes || 0 == bytes.length)
            return i ;
        for (; i < bytes.length; i++) {
            if (bytes[i] == '\0')
                break;
        }
        return i + 1;
    }

    public static String bytesToHexString(byte[] src) {
        StringBuilder stringBuilder = new StringBuilder("");
        if (src == null || src.length <= 0) {
            return null;
        }
        for (int i = 0; i < src.length; i++) {
            int v = src[i] & 0xFF;
            String hv = Integer.toHexString(v);
            if (hv.length() < 2) {
                stringBuilder.append(0);
            }
            stringBuilder.append(hv);
        }
        return stringBuilder.toString();
    }

    public  Gmtext sm4EncryptByKEK(int keyid, int uiAlgID, String jpucIV, byte[] plainBytes){
        Gmtext cipherdata = new Gmtext();


        if (jpucIV == null || plainBytes == null){
            cipherdata.errorCode= -1;
            cipherdata.text = null;
            return  cipherdata;
        }

        byte[] ivBytes = jpucIV.getBytes();
        if (ivBytes.length != 16 ){
            cipherdata.errorCode= -1;
            cipherdata.text = null;
            return  cipherdata;
        }
        byte[] cipherBytes = new byte[plainBytes.length+16];
        int ret =  smEncryptByKEK(keyid, uiAlgID, ivBytes, plainBytes,plainBytes.length,cipherBytes);
        if (ret <= 0){
            cipherdata.errorCode= ret;
            cipherdata.text = null;
            return cipherdata;
        }
        cipherdata.text = new byte[ret];
        System.arraycopy(cipherBytes,0,cipherdata.text,0,ret);
        cipherdata.errorCode= 0;
        cipherBytes = null;
        return cipherdata;
    }

    public   Gmtext sm4DecryptByKEK(int keyid, int uiAlgID, String jpucIV, byte[] cipherBytes){
        Gmtext plaindata = new Gmtext();
        plaindata.errorCode= 0;

        if (jpucIV == null || cipherBytes == null){
            plaindata.errorCode= -1;
            plaindata.text = null;
            return  plaindata;
        }

        byte[] ivBytes = jpucIV.getBytes();
        if (ivBytes.length != 16 ){
            plaindata.errorCode= -1;
            plaindata.text = null;
            return  plaindata;
        }
        byte[] plainBytes = new byte[cipherBytes.length];

        int ret =  smDecryptByKEK(keyid, uiAlgID, ivBytes, cipherBytes,cipherBytes.length, plainBytes);
        if (ret <= 0){
            plaindata.errorCode= ret;
            plaindata.text = null;
            return plaindata;
        }
        plaindata.text = new byte[ret];
        System.arraycopy(plainBytes,0,plaindata.text,0,ret);
        plaindata.errorCode= 0;
        plainBytes = null;
        return plaindata;
    }

    public   Gmtext sm3Hash(byte[] plaindata, int plainlen){

        Gmtext hashdata = new Gmtext();
        if (plaindata == null || plaindata.length == 0){
            hashdata.errorCode= -1;
            hashdata.text = null;
            return hashdata;
        }
        hashdata.text = new byte[32];
        int ret = sm3MereHash(plaindata,plainlen,hashdata.text);
        if (ret < 0){
            hashdata.errorCode= ret;
            hashdata.text = null;
            return hashdata;
        }
        hashdata.errorCode= 0;
        return hashdata;
    }

    public  Gmtext sm4Mac(int keyid, String jpucIV, byte[] jplaindata){

        Gmtext macdata = new Gmtext();
        if (jpucIV == null || jplaindata == null){
            macdata.errorCode= -1;
            macdata.text = null;
            return macdata;
        }

        byte[] ivBytes = jpucIV.getBytes();
        if (ivBytes.length != 16) {
            macdata.errorCode= -1;
            macdata.text = null;
            return macdata;
        }

        byte[] macBytes = new byte[jplaindata.length+16];
        int ret = smMac( keyid,ivBytes,jplaindata,jplaindata.length,macBytes);
        if (ret < 0){
            macdata.errorCode= ret;
            macdata.text = null;
            return macdata;
        }
        macdata.text = new byte[ret];
        System.arraycopy(macBytes,0,macdata.text,0,ret);
        macdata.errorCode= 0;
        macBytes = null;
        return macdata;
    }


    public  Gmtext hmacSm3(int keyid, byte[] jplaindata){

        Gmtext macdata = new Gmtext();
        if (jplaindata == null){
            macdata.errorCode= -1;
            macdata.text = null;
            return macdata;
        }

        byte[] macBytes = new byte[32];
        int ret = hmacSm3( keyid,jplaindata,jplaindata.length,macBytes);
        if (ret < 0){
            macdata.errorCode= ret;
            macdata.text = null;
            return macdata;
        }
        macdata.text = new byte[ret];
        System.arraycopy(macBytes,0,macdata.text,0,ret);
        macdata.errorCode= 0;
        macBytes = null;
        return macdata;
    }

    public  Gmtext sm4EncryptByDerivedKey(int keyid, String jderiveKeyId, int uiAlgID, String jpucIV, byte[] jplaindata){
        Gmtext cipherData = new Gmtext();
        if (jderiveKeyId == null  || jpucIV == null || jplaindata == null){
            cipherData.errorCode= -1;
            cipherData.text = null;
            return cipherData;
        }
        byte[] deriveKeyIdBytes = jderiveKeyId.getBytes();
        byte[] ivBytes = jpucIV.getBytes();
        if (deriveKeyIdBytes.length >= 64 || ivBytes.length != 16){
            cipherData.errorCode= -1;
            cipherData.text = null;
            return cipherData;
        }
        byte[] deriveKeyIdBytesCopy = new byte[deriveKeyIdBytes.length+1];
        Arrays.fill(deriveKeyIdBytesCopy,(byte)0);
        System.arraycopy(deriveKeyIdBytes,0,deriveKeyIdBytesCopy,0,deriveKeyIdBytes.length);
        byte[] cipherBytes = new byte[jplaindata.length+16];
        int ret = smEncryptByDerivedKey(keyid,deriveKeyIdBytesCopy,uiAlgID,ivBytes,jplaindata,jplaindata.length,cipherBytes);
        if (ret < 0){
            cipherData.text = null;
            cipherData.errorCode= ret;
            return cipherData;
        }
        cipherData.text = new byte[ret];
        System.arraycopy(cipherBytes,0,cipherData.text,0,ret);
        cipherData.errorCode= 0;
        return cipherData;
    }

    public Gmtext sm4DecryptByDerivedKey(int keyid, String jderiveKeyId, int uiAlgID, String jpucIV, byte[] jcipherdata){
        Gmtext plainData = new Gmtext();
        if (jderiveKeyId == null  || jpucIV == null || jcipherdata == null){
            plainData.errorCode= -1;
            plainData.text = null;
            return plainData;
        }
        byte[] deriveKeyIdBytes = jderiveKeyId.getBytes();
        byte[] ivBytes = jpucIV.getBytes();
        if (deriveKeyIdBytes.length >= 64 || ivBytes.length != 16){
            plainData.errorCode= -1;
            plainData.text = null;
            return plainData;
        }
        byte[] decBytes = new byte[jcipherdata.length];
        byte[] deriveKeyIdBytesCopy = new byte[deriveKeyIdBytes.length+1];
        Arrays.fill(deriveKeyIdBytesCopy,(byte)0);
        System.arraycopy(deriveKeyIdBytes,0,deriveKeyIdBytesCopy,0,deriveKeyIdBytes.length);
        int ret = smDecryptByDerivedKey(keyid,deriveKeyIdBytesCopy,uiAlgID,ivBytes,jcipherdata,jcipherdata.length,decBytes);
        if (ret < 0){
            plainData.text = null;
            plainData.errorCode= ret;
            return plainData;
        }
        plainData.text = new byte[ret];
        System.arraycopy(decBytes,0,plainData.text,0,ret);
        plainData.errorCode= 0;
        return plainData;
    }

    public   Gmtext sm3Hash2Sign(byte[] plaindata, ECCrefPublicKey publicKey){
        Gmtext hashData = new Gmtext();

        if (plaindata == null || publicKey == null ){
            hashData.text = null;
            hashData.errorCode= -1;
            return hashData;
        }
        hashData.text = new byte[32];
        byte[] px = new byte[32];
        byte[] py = new byte[32];
        System.arraycopy(publicKey.x,32,px,0,32);
        System.arraycopy(publicKey.y,32,py,0,32);

        byte[] uidBytes = "1234567812345678".getBytes();

        int ret =sm3Hash2Sign(plaindata,plaindata.length,px,py,uidBytes,uidBytes.length,hashData.text);
        if (ret != 0){
            hashData.text = null;
            hashData.errorCode= ret>0?-ret:ret;
        }
        hashData.errorCode= 0;
        return hashData;
    }

    public  int  sm2Sign(byte[] hash,ECCSignature signature){
        if (hash == null || hash.length != 32 || signature == null){
            return -1;
        }
        byte[] r = new byte[32];
        byte[] s = new byte[32];

        int ret = sm2Sign(hash,r,s);
        if (ret != 0){
            signature = null;
            return ret>0?-ret:ret;
        }else{
            Arrays.fill(signature.r,(byte)0);
            Arrays.fill(signature.s,(byte)0);
            System.arraycopy(r,0,signature.r,32,32);
            System.arraycopy(s,0,signature.s,32,32);
        }
        return 0;
    }

   public  int sm2Verify(ECCrefPublicKey publicKey, byte[] hash, ECCSignature signature){
       if (publicKey == null || hash == null || hash.length !=  32 || signature == null){
           return -1;
       }
       byte[] px = new byte[32];
       byte[] py = new byte[32];
       System.arraycopy(publicKey.x,32,px,0,32);
       System.arraycopy(publicKey.y,32,py,0,32);

       byte[] r = new byte[32];
       byte[] s = new byte[32];
       System.arraycopy(signature.r,32,r,0,32);
       System.arraycopy(signature.s,32,s,0,32);
       int ret =  sm2Verify(px,py,hash,r,s);
       return ret>0?-ret:ret;
   }

    /**
     * 加密文件
     * @param keyid getKey返回的id
     * @param iv 初始向量
     * @param deriveKeyId 密钥派生id
     * @param inFilePath 输出文件路径
     * @param outFilePath 输出文件路径
     * @param  cryptMode 0 加密, 1 解密
     * @return 返回0表示加密成功
     * 分片加密，默认使用CBC模式，密文最多增加16字节
     */
    private int sm4CryptFile(int keyid,String iv,String deriveKeyId,String inFilePath,String outFilePath,int cryptMode){
        FileInputStream fileInputStream = null;
        FileOutputStream fileOutputStream = null;
        try{
            int pieceSize = 8*1024;
            byte[] inBytes = new byte[pieceSize],outBytes = new byte[pieceSize];
            File inFile= new File(inFilePath);
            File outFile = new File(outFilePath);
            if (inFile.exists() && inFile.isFile() && inFile.canRead()){

            }else{//文件不存在或者文件不可读
                return -1;
            }
            if (outFile.exists()){
                if (!outFile.canWrite()){//文件存在但是文件不可写
                    return -1;
                }
                if (!outFile.isFile()){
                    outFile = new File(outFilePath);
                }
                FileWriter fileWriter =new FileWriter(outFile);
                fileWriter.write("");
                fileWriter.flush();
            }else{
                outFile = new File(outFilePath);
                if (outFile == null){
                    return -1;
                }
            }

            int index = 0,readSize = 0,needReadSize = pieceSize;
            fileInputStream = new FileInputStream(inFilePath);
            fileOutputStream = new FileOutputStream(outFilePath);

            while ((readSize = fileInputStream.read(inBytes,index,needReadSize)) != -1){
                if (readSize == needReadSize){//读到所需字节数
                    if(readSize == pieceSize || readSize+index == pieceSize){//分片满了
                        Gmtext outText = null;
                        int ret = 0;
                        if (cryptMode == 0){
                            if (deriveKeyId == null || deriveKeyId.length() == 0){
                                ret = smEncryptByKEK(keyid,GmAlgID.OFB.value,iv.getBytes(),inBytes,inBytes.length,outBytes);
                            }else{
                                ret = smEncryptByDerivedKey(keyid,deriveKeyId.getBytes(),GmAlgID.OFB.value,iv.getBytes(),inBytes,inBytes.length,outBytes);
                            }
                        }else{
                            if (deriveKeyId == null || deriveKeyId.length() == 0){
                                ret = smDecryptByKEK(keyid,GmAlgID.OFB.value,iv.getBytes(),inBytes,inBytes.length,outBytes);
                            }else{
                                ret = smDecryptByDerivedKey(keyid,deriveKeyId.getBytes(),GmAlgID.OFB.value,iv.getBytes(),inBytes,inBytes.length,outBytes);
                            }
                        }
                        if (ret > 0){
                            fileOutputStream.write(outBytes);
//                            fileOutputStream.flush();
                        }else{
                            fileInputStream.close();
                            fileOutputStream.close();
                            return -1;
                        }
                    }else{
                        System.out.println("error line");
                    }
                }else{//分片没读满,接着读
                    needReadSize = pieceSize-index-readSize;
                    index += readSize;
                }
            }
            if (index>0){
                Gmtext outText = null;
                if (cryptMode == 0){
                    outText =   sm4EncryptByKEK(keyid, GmAlgID.OFB.value,iv,inBytes);
                }else{
                    outText =   sm4DecryptByKEK(keyid, GmAlgID.OFB.value,iv,inBytes);
                }
                fileOutputStream.write(outText.text,0,index);
            }

            fileOutputStream.flush();
            fileInputStream.close();
            fileOutputStream.close();

        }catch (FileNotFoundException e){
            e.printStackTrace();
            return -1;
        }catch (IOException e){
            e.printStackTrace();
            return -1;
        }

        return 0;
    }

    /**
     *解密文件
     * @param keyid getKey返回的id
     *@param iv 初始向量
     * @param deriveKeyId 密钥派生id
     * @param plainFilePath 待加密的文件路径
     * @param cipherFilePath 密文文件路径
     * @return 返回0表示加密成功
     * 分片加密，默认使用CBC模式，密文最多增加16字节
     */
    public int sm4EncryptFile(int keyid,String iv,String deriveKeyId,String plainFilePath,String cipherFilePath){
        return sm4CryptFile(keyid,iv,deriveKeyId,cipherFilePath,plainFilePath,0);
    }
    /**
     *解密文件
     * @param keyid getKey返回的id
     * @param iv 初始向量
     *@param deriveKeyId 密钥派生id
     * @param cipherFilePath 待加密的文件路径
     * @param  plainFilePath 密文文件路径
     * @return 返回0表示加密成功
     * 分片加密，默认使用CBC模式，密文最多增加16字节
     */
    public int sm4DecryptFile(int keyid,String iv,String deriveKeyId,String cipherFilePath,String plainFilePath){
        return sm4CryptFile(keyid,iv,deriveKeyId,cipherFilePath,plainFilePath,1);
    }


    public enum GmAlgID {
        ECB(0x00002001), //密文长度会补位，明文长度不足16字节整数倍会补足16字节整数倍。密文比明文长长度最多长16字节
        CBC(0x00002002), //密文长度会补位，明文长度不足16字节整数倍会补足16字节整数倍。密文比明文长长度最多长16字节
        OFB(0x00002008);   //密文长度与明文长度一致
        public int value;
        private GmAlgID(int value) {
            this.value = value;
        }
    }

    public enum GmKeyProtocol {
        HTTP(0),
        TCP(1);
        public int value;
        private GmKeyProtocol(int value) {
            this.value = value;
        }
    }

    public static class GMCryptoModulePara {

        public int connectType = 0;
        public String deviceSn;
        public String serverAddr;
        public int port = 0;
        public String  configPath;
        public String logPath;
        public byte[] resv;

        public GMCryptoModulePara() {
            connectType = 0;
            deviceSn = "";
            serverAddr = "";
            port = 0;
            configPath = "";
            logPath = "";
            resv = new byte[128];
        }

        public int getconnectType() {
            return connectType;
        }
        public String getdeviceSn() {
            return deviceSn;
        }
        public String getserverAddr() {
            return serverAddr;
        }
        public int getport() {
            return port;
        }
        public String getconfigPath() {
            return configPath;
        }
        public String getlogPath() {
            return logPath;
        }
        public byte[] getresv() {
            return resv;
        }

        @Override
        public String toString() {
            String s = "***:\n";
            s +=("deviceSn:");
            s +=(deviceSn);
            s +=("\nserverAddr:");
            s +=(serverAddr);
            s +=("\nconnectType = " + Long.toHexString(connectType));
            s +=("\nport = " + Long.toHexString(port));
            s +=("\nlogPath:");
            s +=(logPath);
            s +=("\nconfigPath:");
            s +=(configPath);
            s +=("\n");
            return s;
        }
    }


    public static class GmKey {
        public String keyVersion;//KMS密钥版本号
        public int keyId;//密钥获取后，存放在内存中，keyId可对应到特定版本密钥。在加密、解密中传入本id，等效于传入该版本的密钥.
        public String deriveKeyId;//派生密钥的字符串，KMS可根据keyVersion与deriveKeyId动态生成新的密钥.用于一文件一密钥、一会话一密钥
    }

    public static class GmKMSModule {
        public int transType;
        public String      deviceSn;
        public String      kmsServerAddr;
        public int kmsPort;
        public String      syncServerAddr;
        public int syncPort;
        public String      serverKeyPath;
        public String      configPath;
        public String      logPath;
        public String kmsUrl;
        public String signUrl;
        private int deviceType;
        public String organizationId;
        public String appId;
        public String apiKey;
        public String authCode;


        public char []     resv = new char[128];

        public GmKMSModule() {
            transType = 0;
            deviceSn = "";
            kmsServerAddr = "";
            kmsPort = 0;
            syncServerAddr = "";
            syncPort = 0;
            serverKeyPath = "";
            configPath = "";
            logPath = "";
            deviceType = 11;
            authCode = "";
        }
        public void setDeviceType(int deviceType) {
            this.deviceType = deviceType;
        }
    }


    public static class GMKMSModulePara {

        public int transType = 0;
        public String deviceSn;
        public String  kmsServerAddr;
        public int kmsPort = 0;
        public String  syncServerAddr;
        public int syncPort = 0;
        public String serverKeyPath;
        public String configPath;
        public String logPath;
        public String kmsUrl;
        public String signUrl;
        public byte[] resv;
        public GMKMSModulePara() {
            transType = 0;
            deviceSn = "";
            kmsServerAddr = "";
            kmsPort = 0;
            syncServerAddr = "";
            syncPort = 0;
            serverKeyPath = "";
            configPath = "";
            logPath = "";
            resv = new byte[128];
        }

        public String getKmsUrl() {
            return kmsUrl;
        }

        public void setKmsUrl(String kmsUrl) {
            this.kmsUrl = kmsUrl;
        }

        public String getSignUrl() {
            return signUrl;
        }

        public void setSignUrl(String signUrl) {
            this.signUrl = signUrl;
        }

        public int gettransType() {
            return transType;
        }

        public String getdeviceSn() {
            return deviceSn;
        }

        public String getkmsServerAddr() {
            return kmsServerAddr;
        }

        public int getkmsPort() {
            return kmsPort;
        }

        public String getsyncServerAddr() {
            return syncServerAddr;
        }

        public int syncPort() {
            return syncPort;
        }
        public String getserverKeyPath() {
            return serverKeyPath;
        }

        public String getconfigPath() {
            return configPath;
        }

        public String getlogPath() {
            return logPath;
        }

        public byte[] getresv() {
            return resv;
        }


        public void printGMKMSModulePara() {
            System.out.println("GMKMSModulePara:");
            System.out.println("deviceSn:"+deviceSn);
            System.out.println("\nkmsServerAddr:"+kmsServerAddr);
            System.out.println("\nsyncServerAddr:"+syncServerAddr);
            log.info("\n");
        }

        @Override
        public String toString(){
            String s = "deviceSn:\n";
            s += deviceSn;
            s += "\nkmsServerAddr:\n";
            s += kmsServerAddr;
            s += "\nsyncServerAddr:\n";
            s += syncServerAddr;
            return s;
        }
    }

    public static class Gmtext {
        public byte[] text;
        public int errorCode;
    }

    public static class ECCSignature {

        private byte[] r = new byte[64];
        private byte[] s = new byte[64];

        public ECCSignature() {

        }

        public int isEqual(ECCSignature a) {
            for (int i = 0; i < 64; i++) {
                if (a.r[i] != r[i]) {
                    return -1;
                }
            }
            for (int i = 0; i < 64; i++) {
                if (a.s[i] != s[i]) {
                    return -1;
                }
            }
            return 0;
        }

        public void printECCSignature() {
            log.info("ECCSignature:\n");
            log.info("\nr:");
            for (int i = 0; i < 64; i++) {
                log.info("{}", Integer.toHexString(r[i] & 0xff));
            }

            log.info("\ns:");
            for (int i = 0; i < 64; i++) {
                log.info("{}", Integer.toHexString(s[i] & 0xff));
            }
            log.info("\n");
        }

        public static String hex(byte[] bytes) {
            StringBuilder result = new StringBuilder();
            for (byte aByte : bytes) {
                int decimal = (int) aByte & 0xff;               // bytes widen to int, need mask, prevent sign extension
                // get last 8 bits
                String hex = Integer.toHexString(decimal);
                if (hex.length() % 2 == 1) {                    // if half hex, pad with zero, e.g \t
                    hex = "0" + hex;
                }
                result.append(hex);
            }
            return result.toString();
        }

        public  String bytesToHexString(byte[] src) {
            StringBuilder stringBuilder = new StringBuilder("");
            if (src == null || src.length <= 0) {
                return null;
            }
            for (int i = 0; i < src.length; i++) {
                int v = src[i] & 0xFF;
                String hv = Integer.toHexString(v);
                if (hv.length() < 2) {
                    stringBuilder.append(0);
                }
                stringBuilder.append(hv);
            }
            return stringBuilder.toString();
        }

        @Override
        public String toString(){
            String s = "";
            s += hex(r);
            s += hex(this.s);
            return s;
        }
    }

    public static class ECCrefPrivateKey {
        private int bits;
        private byte[] D;

        public ECCrefPrivateKey() {
            bits = 256;
            D = new byte[64];
        }

        public ECCrefPrivateKey(int bits, byte[] d) {
            this.bits = bits;
            D = d;
        }

        public int getBits() {
            return bits;
        }

        public void setBits(int bits) {
            this.bits = bits;
        }

        public byte[] getD() {
            return D;
        }

        public void setD(byte[] d) {
            D = d;
        }

        public void printECCrefPrivateKey() {
            log.info("ECCrefPrivateKey:{}\n", bits);
            log.info("D:");
            for (int i = 0; i < 64; i++) {
                log.info("{}", Integer.toHexString(D[i] & 0xff));
            }
            log.info("\n");
        }

        public  String bytesToHexString(byte[] src) {
            StringBuilder stringBuilder = new StringBuilder("");
            if (src == null || src.length <= 0) {
                return null;
            }
            for (int i = 0; i < src.length; i++) {
                int v = src[i] & 0xFF;
                String hv = Integer.toHexString(v);
                if (hv.length() < 2) {
                    stringBuilder.append(0);
                }
                stringBuilder.append(hv);
            }
            return stringBuilder.toString();
        }

        @Override
        public String toString(){
            String s = "bits:\n";
            s += bits;
            s += "\nD:\n";
            s += bytesToHexString(D);
            return s;
        }
    }

    public static class ECCrefPublicKey {
        private int bits;
        private byte[] x = new byte[64];
        private byte[] y = new byte[64];

        public ECCrefPublicKey() {
        }

        public int isEqual(ECCrefPublicKey a) {
            if (a.bits != bits) {
                return -1;
            }
            for (int i = 0; i < 64; i++) {
                if (a.x[i] != x[i]) {
                    return -1;
                }
            }
            for (int i = 0; i < 64; i++) {
                if (a.y[i] != y[i]) {
                    return -1;
                }
            }

            return 0;
        }

        public void printECCrefPublicKey() {
            log.info("ECCrefPublicKey:{}\n", bits);
            System.out.println("x:");
            for (int i = 0; i < 64; i++) {
                log.info("{}", Integer.toHexString(x[i] & 0xff));
            }
            System.out.println("\ny:");
            for (int i = 0; i < 64; i++) {
                log.info("{}", Integer.toHexString(y[i] & 0xff));
            }
            log.info("\n");
        }

        public  String bytesToHexString(byte[] src) {
            StringBuilder stringBuilder = new StringBuilder("");
            if (src == null || src.length <= 0) {
                return null;
            }
            for (int i = 0; i < src.length; i++) {
                int v = src[i] & 0xFF;
                String hv = Integer.toHexString(v);
                if (hv.length() < 2) {
                    stringBuilder.append(0);
                }
                stringBuilder.append(hv);
            }
            return stringBuilder.toString();
        }

        @Override
        public String toString(){
            String s = "bits:\n";
            s += bits;
            s += "\nx:\n";
            s += bytesToHexString(x);
            s += "\ny:\n";
            s += bytesToHexString(y);
            return s;
        }
    }

}
